/**
 * Misc - Miscellaneous and Sundry Utility Functions
 *
 * @author Chris Pratt
 *
 * 3/5/1999
 */
package com.anodyzed.onyx.util;

import java.util.Collection;
import java.util.Date;
import java.util.Map;

public class Misc {
  public static final long MS  = 1;
  public static final long SEC = 1000 * MS;
  public static final long MIN = 60   * SEC;
  public static final long HR  = 60   * MIN;
  public static final long DAY = 24   * HR;
  public static final long WK  = 7    * DAY;
  public static final long YR  = 365  * DAY;
  public static final long LYR = 4    * YR;

  /**
   * Protected Constructor
   */
  protected Misc () {
  } //Misc

  ////////// Numeric Identification //////////

  /**
   * Test whether a character is a numeric digit in the specified base
   *
   * @param c The Character in Question
   * @param base The Numeric Base
   * @return true if the character is a numeric digit in base
   */
  public static boolean isNumeric (char c,int base) {
    if(base > 10) {
      char C;
      return ((c >= '0') && (c <= '9')) || (((C = Character.toUpperCase(c)) >= 'A') && (C <= (((char)base - 10) + 'A')));
    } else {
      return (c >= '0') && (c <= (((char)base - 1) + '0'));
    }
  } //isNumeric

  /**
   * Test whether a String is Numeric
   *
   * @param str The Test String
   * @return true if all the characters are numeric
   */
  public static boolean isNumeric (String str) {
    if((str != null) && (str.length() > 0)) {
      char[] ch = str.toCharArray();
      for(char c : ch) {
        if(!Character.isDigit(c)) {
          return false;
        }
      }
      return true;
    }
    return false;
  } //isNumeric

  ////////// Object Management //////////

  /**
   * Test whether the supplied String contains any non-whitespace characters
   * 
   * @param str The String to test
   * @return true if the supplied string is null, or contains <b>only</b> 
   *          whitespace characters
   */
  public static boolean isEmpty (String str) {
    return (str == null) || (str.trim().length() <= 0);
  } //isEmpty

  /**
   * Test whether the supplied Array contains any elements
   *
   * @param ary The Array to test
   * @return true if the supplied Array is null or contains no elements
   */
  public static <T> boolean isEmpty (T[] ary) {
    return (ary == null) || (ary.length == 0);
  } //isEmpty

  /**
   * Test whether the supplied Collection contains any elements
   *
   * @param col The Collection to test
   * @return true if the supplied Collection is null or empty
   */
  public static boolean isEmpty (Collection<?> col) {
    return (col == null) || col.isEmpty();
  } //isEmpty

  /**
   * Test whether the supplied Map contains any elements
   *
   * @param map The Map to test
   * @return true if the supplied Map is null or empty
   */
  public static boolean isEmpty (Map<?,?> map) {
    return (map == null) || map.isEmpty();
  } //isEmpty

  /**
   * Test two values for equality.  Differs from o1.equals(o2) only in
   * that it copes with <tt>null</tt> o1 properly.
   *
   * @param o1 The first Object to test
   * @param o2 The second Object to test
   * @return true if the objects are equivalent
   */
  public static boolean equals (Object o1, Object o2) {
    return (o1 == null) ? (o2 == null) : o1.equals(o2);
  } //equals

  ////////// Array Manipulation //////////

  /**
   * Find the Index of an element of an Array
   *
   * @param ary The Object Array
   * @param item The Object to search for
   * @param start The index to start searching from 
   * @return The index of <code>item</code> in <code>ary</code> (or -1 if not
   *         found)
   */
  public static int arrayIndexOf (Object[] ary,Object item,int start) {
    if(item != null) {
      for(int i = start;i < ary.length;i++) {
        if(item.equals(ary[i])) {
          return i;
        }
      }
    } else {
      for(int i = start;i < ary.length;i++) {
        if(ary[i] == null) {
          return i;
        }
      }
    }
    return -1;
  } //arrayIndexOf

  /**
   * Find the Index of an element of an Array
   *
   * @param ary The Object Array
   * @param item The Object to search for
   * @return The index of <code>item</code> in <code>ary</code> (or -1 if not
   *         found)
   */
  public static int arrayIndexOf (Object[] ary,Object item) {
    return arrayIndexOf(ary,item,0);
  } //arrayIndexOf

  /**
   * Find the Index of an element of an Array
   *
   * @param ary The byte Array
   * @param b The byte to search for
   * @param start The index to start searching from 
   * @return The index of <code>b</code> in <code>ary</code> (or
   *         -1 if not found)
   */
  public static int arrayIndexOf (byte[] ary,byte b,int start) {
    for(int i = start;i < ary.length;i++) {
      if(b == ary[i]) {
        return i;
      }
    }
    return -1;
  } //arrayIndexOf

  /**
   * Find the Index of an element of an Array
   *
   * @param ary The byte Array
   * @param b The byte to search for
   * @return The index of <code>b</code> in <code>ary</code> (or
   *         -1 if not found)
   */
  public static int arrayIndexOf (byte[] ary,byte b) {
    return arrayIndexOf(ary,b,0);
  } //arrayIndexOf

  /**
   * Find the Index of an element of an Array
   *
   * @param ary The character Array
   * @param ch The character to search for
   * @param start The index to start searching from 
   * @return The index of <code>ch</code> in <code>ary</code> (or
   *         -1 if not found)
   */
  public static int arrayIndexOf (char[] ary,char ch,int start) {
    for(int i = start;i < ary.length;i++) {
      if(ch == ary[i]) {
        return i;
      }
    }
    return -1;
  } //arrayIndexOf

  /**
   * Find the Index of an element of an Array
   *
   * @param ary The character Array
   * @param ch The character to search for
   * @return The index of <code>ch</code> in <code>ary</code> (or
   *         -1 if not found)
   */
  public static int arrayIndexOf (char[] ary,char ch) {
    return arrayIndexOf(ary,ch,0);
  } //arrayIndexOf

  ////////// Chronographic Utilities //////////

  /**
   * Append the next division to the duration string
   *
   * @param buf The String Buffer
   * @param dur The division duration
   * @param div The division name
   * @param spc if true, prepend a space
   * @return the new space value
   */
  private static boolean append (StringBuilder buf,long dur,String div,boolean spc) {
    if(spc) {
      buf.append(' ');
    }
    buf.append(dur).append(' ').append(div);
    if(dur != 1) {
      buf.append('s');
    }
    return true;
  } //append

  /**
   * Return the duration as a string description of a length of time
   *
   * @param time The duration length (in ms)
   * @return The duration description String
   */
  public static String duration (long time) {
    StringBuilder buf = new StringBuilder();
    if(time < 0) {
      time *= -1;
    }
    if(time > 0) {
      long dur;
      boolean space = false;
      if(time >= YR) {
        dur = time / LYR;
        time %= LYR;
        space = append(buf,(dur * 4) + (time / YR),"year",space);
        time %= YR;
      }
      if(time >= WK) {
        space = append(buf,time / WK,"week",space);
        time %= WK;
      }
      if(time >= DAY) {
        space = append(buf,time / DAY,"day",space);
        time %= DAY;
      }
      if(time >= HR) {
        space = append(buf,time / HR,"hour",space);
        time %= HR;
      }
      if(time >= MIN) {
        space = append(buf,time / MIN,"minute",space);
        time %= MIN;
      }
      if(time >= SEC) {
        space = append(buf,time / SEC,"second",space);
        time %= SEC;
      }
      if(time >= MS) {
        append(buf,time,"millisecond",space);
      }
    } else {
      buf.append("0 milliseconds");
    }
    return buf.toString();
  } //duration

  /**
   * Return the length of time between two points as a string description
   *
   * @param start The starting time (in ms)
   * @param stop The ending time (in ms)
   * @return The duration description String
   */
  public static String duration (long start,long stop) {
    return duration(stop - start);
  } //duration

  /**
   * Return the length of time between two Dates as a string description
   *
   * @param start The starting Date
   * @param stop The ending Date
   * @return The duration description String
   */
  public static String duration (Date start,Date stop) {
    return duration(stop.getTime() - start.getTime());
  } //duration

  /**
   * Return the Period Duration string
   *
   * @param period The Time Period
   * @return The duration description String
   */
  public static String duration (Period period) {
    return duration(period.getDurationInMillis());
  } //duration

} //*Misc
